<template>
  <div class="wrap">
    <div class="title-wrap">
      <div :class="[selfRound ? 'self' : 'other']">{{ selfRound ? "自己回合" : "对方回合" }}</div>
    </div>
    <canvas
      :class="[selfRound ? 'self' : 'other']"
      ref="canvasBoard"
      id="board"
      width="600"
      height="600"
    ></canvas>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, onBeforeUnmount, ref, reactive, watch, defineEmits } from "vue"
import { ElMessageBox } from "element-plus"
import io from "socket.io/client-dist/socket.io"
import { createIO } from "@/socket/client"
const IO = createIO(io)

const canvasBoard = ref("board")
const N = ref(20)
let matrix = reactive(
  new Array(N.value + 1).fill(0).map(_ => Array.from({ length: N.value + 1 }).fill(0))
) as number[][] // 棋盘实时信息
let matrixHistory = reactive([]) // 每步棋落子记录
let canvasContext: any // 画布执行上下文
let canvasWidth: any // 画布宽度
let canvasHeight: any // 画布高度
let spacing: number // 线条间距
let pieceSize: number // 棋子大小
let pieceColor: string[] = ["white", "black"] // 棋子颜色
let pieceNum = ref(0) // 棋子数量
let padding: number = 20 // 棋盘内边距
let selfRound = ref(false) // 是否是自己回合
let boardNotice = ref("白棋先行") // 棋盘提示
let currentIp = ref("") // 当前IP
let targetIp = ref("") // 对手IP

const emit = defineEmits(["goBack"])

watch(matrix, newVal => {}, { deep: true, immediate: true })

onMounted(() => {
  canvasContext = ((canvasBoard.value as unknown) as HTMLCanvasElement).getContext("2d")
  canvasWidth = ((canvasBoard.value as unknown) as HTMLCanvasElement).width
  canvasHeight = ((canvasBoard.value as unknown) as HTMLCanvasElement).height

  clearBoardAndInit()

  matrix[5][5] = 2

  addServerListener()
})

onBeforeUnmount(() => {
  ;((canvasBoard.value as unknown) as HTMLElement).removeEventListener("click", getClickPoint)
})

function addServerListener() {
  IO.on("address", (ip: string) => {
    currentIp.value = ip
  })

  IO.on("battle", order => {
    selfRound.value = Boolean(order)
  })

  IO.on("update:board", data => {
    if (data.targetIp === currentIp.value) {
      pieceNum.value = data.pieceNums - 1
      drawPiece(data.point)
      selfRound.value = true
    }
  })

  IO.on("rival:outline", ip => {
    if (ip === currentIp.value) {
      ElMessageBox.alert("对方逃跑了。", "游戏结束", {
        confirmButtonText: "确定",
        showClose: false,
        callback: () => {
          emit("goBack")
        },
      })
    }
  })
}

function sendMsgToServer(point) {
  selfRound.value = false
  IO.emit("update:board", {
    point: point,
    pieceNums: pieceNum.value,
  })
}

function getClickPoint(e: MouseEvent) {
  if (!selfRound.value) return
  // console.log(e.offsetX, e.offsetY) // 鼠标相对于棋盘的偏移量
  let x = e.offsetX - padding + spacing / 2
  let y = e.offsetY - padding + spacing / 2

  // 根据偏移量计算：点击位置 距离棋盘最近的点
  const targetPoint = { x: Math.floor(y / spacing), y: Math.floor(x / spacing) }
  drawPiece(targetPoint)
  sendMsgToServer(targetPoint)
}

function drawBoard(n = 10, padding = 20) {
  spacing = (canvasWidth - padding * 2) / n
  pieceSize = spacing * 0.8
  for (let i = padding; i < canvasWidth; i += spacing) {
    drawLine(i, padding, i, canvasHeight - padding, 2)
  }

  for (let i = padding; i < canvasHeight; i += spacing) {
    drawLine(padding, i, canvasWidth - padding, i, 2)
  }
}

function clearBoardAndInit() {
  if (canvasContext) {
    pieceNum.value = 0
    canvasContext.clearRect(0, 0, 600, 600)
    drawBoard(N.value, padding)
    ;((canvasBoard.value as unknown) as HTMLElement).addEventListener("click", getClickPoint)
  }
}

function drawLine(x1: number, y1: number, x2: number, y2: number, lineWidth = 1) {
  canvasContext.moveTo(x1, y1)
  canvasContext.lineTo(x2, y2)
  canvasContext.lineWidth = lineWidth
  canvasContext.stroke()
}

type coordinate = {
  x: number
  y: number
}

function drawPiece(ops: coordinate) {
  const { x, y } = ops
  if (matrix[x][y] !== 0) return
  matrix[x][y] = pieceNum.value % 2 ? 1 : 2
  let point = {
    x: y * spacing + padding,
    y: x * spacing + padding,
  }
  canvasContext.beginPath()
  canvasContext.arc(point.x, point.y, pieceSize / 2, 0, 2 * Math.PI)
  canvasContext.stroke()
  canvasContext.fillStyle = pieceColor[pieceNum.value % 2]
  canvasContext.fill()
  pieceNum.value++
  if (checkIsWin(x, y)) {
    ElMessageBox.alert(matrix[x][y] === 2 ? "白棋获胜！" : "黑棋获胜！", "游戏结束", {
      confirmButtonText: "确定",
      showClose: false,
      callback: () => {
        emit("goBack")
      },
    })
    // console.log(matrix[x][y] === 2 ? "白棋获胜" : "黑棋获胜")
    return
  }
}

function checkIsWin(x: number, y: number) {
  // 在点{x,y} 四个方向判断是否有连续5个相同值的
  const t = matrix[x][y]

  // 纵方向
  let i, j, cnt
  for (cnt = 0, i = y; i >= 0 && matrix[x][i] === t; i--, cnt++);
  for (i = y + 1; i <= N.value && matrix[x][i] === t; i++, cnt++);
  if (cnt >= 5) return true

  // 横方向
  for (cnt = 0, i = x; i >= 0 && matrix[i][y] === t; i--, cnt++);
  for (i = x + 1; i <= N.value && matrix[i][y] === t; i++, cnt++);
  if (cnt >= 5) return true

  // 主对角线方向
  for (cnt = 0, i = x, j = y; i >= 0 && matrix[i][j] === t; i--, j--, cnt++);
  for (i = x + 1, j = y + 1; i <= N.value && matrix[i][j] === t; i++, j++, cnt++);
  if (cnt >= 5) return true

  // 次对角线方向
  for (cnt = 0, i = x, j = y; i <= N.value && j >= 0 && matrix[i][j] === t; i++, j--, cnt++);
  for (i = x - 1, j = y + 1; i >= 0 && j <= N.value && matrix[i][j] === t; i--, j++, cnt++);
  if (cnt >= 5) return true
  // console.log(matrix[x][y])

  return false
}
</script>

<style scoped lang="scss">
.wrap {
  position: fixed;
  width: 100%;
  height: 100%;

  .title-wrap {
    height: 80px;
    display: flex;
    justify-content: center;
    align-items: center;

    div {
      height: 25px;
      padding: 5px 10px;
      border-radius: 5px;
      font-weight: bold;
      line-height: 25px;
      background: #eee;

      &.self {
        background: #60d576;
      }

      &.other {
        background: #d56060;
      }
    }
  }

  #board {
    position: absolute;
    margin: auto;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    cursor: pointer;
    box-shadow: 0 0 8px #ccc;

    &.self {
      box-shadow: 0 0 8px #60d576;
    }

    &.other {
      box-shadow: 0 0 8px #d56060;
    }
  }
}
</style>
